#include <iostream>
#include <functional>
#include <string>
#include <cstdio>
#include <pthread.h>
#include <unistd.h>
#include <vector>
#include "Thread.hpp"
#include "LockGuard.hpp"

const int threadnum = 5;

void Print(int num)
{
    while (num--)
    {
        std::cout << "new thread runing ! , num : " << num << std::endl;
        sleep(1);
    }
}

std::string GetThreadName()
{
    static int num = 1; // 相当于全局变量
    char threadname[64];
    snprintf(threadname, sizeof(threadname), "Thread-%d", num);
    return threadname;
}

class ThreadData
{
public:
    ThreadData(const std::string &name, pthread_mutex_t *lock) : _name(name), _lock(lock)
    {
    }
    std::string _name;
    pthread_mutex_t *_lock;
};

// 模拟抢票过程
int ticket = 10000;
// pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 锁就有了，被定义并初始化了，这个锁也是全局的哦！！

// 加锁：
// 1. 我们要尽可能的给少的代码块加锁
// 2. 一般加锁，都是给临界区加
// 3. 个别系统，抢票代码会出现很多的票被同一个线程抢完了
// 4. 多线程运行，同一份资源，有线程长时间无法拥有，饥饿问题
// 5. 要解决饥饿问题，要让线程执行的时候，具备一定的顺序性 --- 同步
void GetTicket(ThreadData* td)
{
    while (1)
    {
        {
            LockGuard lockguard(td->_lock);
            // pthread_mutex_lock(mutex);
            // 根据互锁的定义，任何时刻只允许一个执行流访问临界资源，
            // 那么只允许一个线程申请锁成功！多个线程申请锁失败，失败的线程怎么办？在mutex上进行阻塞，本质就是等待！
            if (ticket > 0)
            {
                usleep(1000); // 模拟抢票消耗时间 1ms
                printf("%s , get a ticket : %d\n",td->_name.c_str() ,ticket);
                ticket--; // 这一条代码，不是原子的，在汇编层面有三条
                // pthread_mutex_unlock(mutex);
            }
            else
            {
                // pthread_mutex_unlock(mutex);
                break;
            }
        }
        // todo 可能还有后续操作
    }
}

int main()
{
    pthread_mutex_t mutex;
    pthread_mutex_init(&mutex, nullptr);
    // // 抢票
    // std::string name1 = GetThreadName();
    // Thread<std::string> *t1 = new Thread<std::string>(name1, GetTicket, name1);

    // std::string name2 = GetThreadName();
    // Thread<std::string> *t2 = new Thread<std::string>(name2, GetTicket, name2);

    // std::string name3 = GetThreadName();
    // Thread<std::string> *t3 = new Thread<std::string>(name3, GetTicket, name3);

    // std::string name4 = GetThreadName();
    // Thread<std::string> *t4 = new Thread<std::string>(name4, GetTicket, name4);

    // //抢票
    // std::string name1 = GetThreadName();
    // Thread<pthread_mutex_t *> *t1 = new Thread<pthread_mutex_t *>(name1, GetTicket, &mutex);

    // std::string name2 = GetThreadName();
    // Thread<pthread_mutex_t *> *t2 = new Thread<pthread_mutex_t *>(name2, GetTicket, &mutex);

    // std::string name3 = GetThreadName();
    // Thread<pthread_mutex_t *> *t3 = new Thread<pthread_mutex_t *>(name3, GetTicket, &mutex);

    // std::string name4 = GetThreadName();
    // Thread<pthread_mutex_t *> *t4 = new Thread<pthread_mutex_t *>(name4, GetTicket, &mutex);

    std::string name1 = GetThreadName();
    Thread<ThreadData *> *t1 = new Thread<ThreadData *>(name1, GetTicket, new ThreadData(name1, &mutex));

    std::string name2 = GetThreadName();
    Thread<ThreadData *> *t2 = new Thread<ThreadData *>(name2, GetTicket, new ThreadData(name2, &mutex));

    std::string name3 = GetThreadName();
    Thread<ThreadData *> *t3 = new Thread<ThreadData *>(name3, GetTicket, new ThreadData(name3, &mutex));

    std::string name4 = GetThreadName();
    Thread<ThreadData *> *t4 = new Thread<ThreadData *>(name4, GetTicket, new ThreadData(name4, &mutex));

    t1->Start();
    t2->Start();
    t3->Start();
    t4->Start();

    t1->Join();
    t2->Join();
    t3->Join();
    t4->Join();

    pthread_mutex_destroy(&mutex);

    // // 创建多线程
    // std::vector<Thread<int> *> Threads;

    // for (int i = 0; i < threadnum; i++)
    // {
    //     Threads.push_back(new Thread<int>(GetThreadName(), Print, 10));
    // }

    // for (auto &t : Threads)
    // {
    //     std::cout << t->ThreadName() << "isruning : " << t->IsRuning() << std::endl;
    // }

    // for (auto &t : Threads)
    // {
    //     t->Start();
    // }

    // for (auto &t : Threads)
    // {
    //     std::cout << t->ThreadName() << "isruning : " << t->IsRuning() << std::endl;
    // }

    // for (auto &t : Threads)
    // {
    //     t->Join();
    // }

    // Thread<int> t(GetThreadName(),Print,10);
    // std::cout << "isruning : " << t.IsRuning() << std::endl;
    // t.Start();

    // std::cout << "isruning : " << t.IsRuning() << std::endl;
    // t.Join();
    return 0;
}